﻿namespace SageKing.Blazor.OpenIdService.Services;

using System.Diagnostics;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Options;
using SageKing.Core.Extensions;
using SageKing.OpenIddict;
using static NewLife.Remoting.ApiHttpClient; 
using UserInfo = SageKing.Blazor.OpenIdService.Client.UserInfo;

public class CustomAuthStateProvider : RevalidatingServerAuthenticationStateProvider, IDisposable
{
    private readonly IServiceScopeFactory scopeFactory;
    private AuthenticationState authenticationState;
    private ProtectedLocalStorage _protectedStore;

    private readonly PersistingComponentStateSubscription subscription;

    private readonly IdentityOptions _identityoptions;
    private NavigationManager _NavigationManager;
    public CustomAuthenticationService AuthService { get; private set; }

    public ClaimsPrincipal User => AuthService?.User;

    public bool _hasRedirected = false;

    public CustomAuthStateProvider(
          ILoggerFactory loggerFactory,
          IServiceScopeFactory serviceScopeFactory,
        CustomAuthenticationService service,
        ProtectedLocalStorage protectedStore,
        PersistentComponentState persistentComponentState,
          IOptions<IdentityOptions> optionsAccessor,
        NavigationManager _NavigationManager
        ) : base(loggerFactory)
    {
        this.AuthService = service;
        this._protectedStore = protectedStore;
        this.scopeFactory = serviceScopeFactory;
        this._identityoptions = optionsAccessor.Value;
        this._NavigationManager = _NavigationManager;
        authenticationState = new AuthenticationState(service.User);

        this.AuthService.UserChanged += Service_UserChanged;
    }

    private void Service_UserChanged(ClaimsPrincipal newUser)
    {
        authenticationState = new AuthenticationState(newUser);
        NotifyAuthenticationStateChanged(Task.FromResult(authenticationState));
    }

    public OpenIdServerOptions ServerOption
    {
        get
        {
            return this.AuthService.ServerOption;
        }
    }


    public async Task<bool> SignOut()
    {
        _hasRedirected = false;
        return await AuthService.SignOut();
    }

    protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30);

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {

        if (!authenticationState.User.IsAuthenticated())
        {
            try
            {
                var userSessionStorageResult = await _protectedStore.GetAsync<UserSession>(LocalStorageConsts.UserSessionKey);
                var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
                if (userSession.IsNotNull())
                {
                    var getname = userSession.UserName;
                    if (getname.IsNotNull() && userSession.Data.IsNotNull())
                    {
                        var newUser = userSession.Data.DictionaryToClaimsPrincipal();
                         
                        this.AuthService.User = newUser;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        return await Task.FromResult(authenticationState);
    }
    public void NavigateTo(string url, ClaimsPrincipal User)
    {
        if (!User.IsAuthenticated())
        {
            NavigateTo(url);
        }
    }

    public void NavigateTo(string url)
    {
        lock (this)
        {
            var returnUrl = _NavigationManager.ToBaseRelativePath(url);
            if (string.IsNullOrWhiteSpace(returnUrl))
            {
                _NavigationManager.NavigateTo(OpenIddictUIConstants.LoginURL);
            }
            else
            {
                if (!_hasRedirected && !returnUrl.StartsWith(OpenIddictUIConstants.LoginURL.TrimStart('/')))
                {
                    _hasRedirected = true;
                    _NavigationManager.NavigateTo($"{OpenIddictUIConstants.LoginURL}?returnUrl={returnUrl.UrlEncode()}&flag=authstate", true);
                }
            }
        }
    }


    public void Dispose()
    {
        try
        {
            subscription.Dispose();
            this.AuthService.UserChanged -= Service_UserChanged;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    protected override async Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
    {
        // Get the user manager from a new scope to ensure it fetches fresh data
        await using var scope = scopeFactory.CreateAsyncScope();
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<AppUser>>();
        return await ValidateSecurityStampAsync(userManager, authenticationState.User);
    }
    private async Task<bool> ValidateSecurityStampAsync(UserManager<AppUser> userManager, ClaimsPrincipal principal)
    {
        var user = await userManager.GetUserAsync(principal);
        if (user is null)
        {
            return false;
        }
        else if (!userManager.SupportsUserSecurityStamp)
        {
            return true;
        }
        else
        {
            var principalStamp = principal.FindFirstValue(_identityoptions.ClaimsIdentity.SecurityStampClaimType);
            var userStamp = await userManager.GetSecurityStampAsync(user);
            return principalStamp == userStamp;
        }
    }
}